use std::{collections::HashMap, path::PathBuf};

use im_rc::Vector;

use crate::{
    builtin::{self, Builtin},
    parser::{Expr, Func, IfBranch, Stat, Value},
    ARGV,
};

pub enum Callable {
    Func(Func),
    Builtin(Box<dyn builtin::Builtin>),
}

pub struct FuncBinds {
    binds: HashMap<String, Callable>,
}

impl FuncBinds {
    pub fn new() -> Self {
        Self {
            binds: HashMap::new(),
        }
    }

    pub fn bind_builtin<B: Builtin + 'static>(&mut self, name: &'static str, b: B) {
        self.binds
            .insert(name.to_owned(), Callable::Builtin(Box::new(b)));
    }

    pub fn bind_func<Name: ToString>(&mut self, name: Name, b: Func) {
        self.binds.insert(name.to_string(), Callable::Func(b));
    }

    pub fn take_main_func(&mut self) -> Result<Callable, ()> {
        let Some(main_func) = self.binds.remove("main") else {
            eprintln!("can't find main function");
            return Err(());
        };

        Ok(main_func)
    }
}

pub struct Env {
    cwd: Option<PathBuf>,
    binds: HashMap<String, Value>,
}

impl Env {
    pub fn get_cwd(&self) -> &Option<PathBuf> {
        &self.cwd
    }

    pub fn new(cwd: Option<PathBuf>) -> Self {
        Self {
            cwd,
            binds: [
                ("True".to_string(), Value::Bool(true)),
                ("False".to_string(), Value::Bool(false)),
            ]
            .into_iter()
            .collect(),
        }
    }

    pub fn find(&self, name: &str) -> Option<&Value> {
        if let Some(v) = self.binds.get(name) {
            return Some(v);
        };
        None
    }

    pub fn eval_func_call(
        &self,
        func_binds: &FuncBinds,
        func: &Callable,
        args: &[Expr],
    ) -> Result<Value, ()> {
        let mut argv = Vec::with_capacity(args.len());

        for arg in args.iter() {
            argv.push(self.eval_expr(func_binds, arg)?);
        }

        let mut env = Env::new(self.cwd.clone());

        Ok(match func {
            Callable::Func(func) => {
                env.binds
                    .insert(ARGV.to_string(), Value::Array(argv.into()));

                for stat in &func.stats {
                    match env.eval_stat(func_binds, stat)? {
                        ControlFlow::None => continue,
                        ControlFlow::Return(value) => {
                            return Ok(value);
                        }
                        cf @ ControlFlow::Break | cf @ ControlFlow::Continue => {
                            eprintln!("invalid control flow at function top: {:?}", cf);
                            return Err(());
                        }
                    }
                }

                // 没有 return 语句的函数返回 0。
                Value::Integer(0)
            }
            Callable::Builtin(builtin) => builtin.call(self, &argv)?,
        })
    }

    fn eval_stat(&mut self, func_binds: &FuncBinds, stat: &Stat) -> Result<ControlFlow, ()> {
        Ok(match stat {
            Stat::If {
                branches,
                otherwise,
            } => self.eval_if(func_binds, branches, otherwise)?,

            Stat::Continue => return Ok(ControlFlow::Continue),
            Stat::Break => return Ok(ControlFlow::Break),
            Stat::Return { expr } => {
                return Ok(ControlFlow::Return(self.eval_expr(func_binds, expr)?));
            }

            Stat::Foreach {
                iterator,
                iterable,
                body,
            } => self.eval_foreach(func_binds, iterable, iterator, body)?,

            Stat::CdBlock { path, stats } => self.eval_cd_block(func_binds, path, stats)?,
            Stat::Let { name, expr } => self.eval_let(func_binds, name, expr)?,

            Stat::FuncCall { name, args } => {
                let Some(func) = func_binds.binds.get(name) else {
                    eprintln!("undefined function: {}", name);
                    return Err(());
                };

                self.eval_func_call(func_binds, func, args)?;
                ControlFlow::None
            }

            Stat::While { cond, body } => self.eval_while(func_binds, cond, body)?,
        })
    }

    fn eval_let(
        &mut self,
        func_binds: &FuncBinds,
        name: &str,
        expr: &Expr,
    ) -> Result<ControlFlow, ()> {
        let value = self.eval_expr(func_binds, expr)?;
        let const_val = name.starts_with(char::is_uppercase);

        if self.binds.insert(name.to_owned(), value).is_some() && const_val {
            eprintln!("let: const variable can't be re-assign: {name}");
            return Err(());
        }

        Ok(ControlFlow::None)
    }

    fn eval_while(
        &mut self,
        func_binds: &FuncBinds,
        cond: &Expr,
        body: &[Stat],
    ) -> Result<ControlFlow, ()> {
        loop {
            let cond = self.eval_expr(func_binds, cond)?;

            let cond = match cond {
                Value::Bool(b) => b,
                _ => {
                    eprintln!("condition in while loop is not boolean: {cond:?}");
                    return Err(());
                }
            };

            if !cond {
                break;
            }

            for stat in body {
                match self.eval_stat(func_binds, stat)? {
                    ControlFlow::None => (),
                    ControlFlow::Continue => break,
                    ControlFlow::Return(value) => return Ok(ControlFlow::Return(value)),
                    ControlFlow::Break => return Ok(ControlFlow::None),
                }
            }
        }

        Ok(ControlFlow::None)
    }

    fn eval_if(
        &mut self,
        func_binds: &FuncBinds,
        branches: &Vec<IfBranch>,
        otherwise: &Vec<Stat>,
    ) -> Result<ControlFlow, ()> {
        let mut reach_otherwise = true;

        for branch in branches {
            let cond = self.eval_expr(func_binds, &branch.cond)?;

            let b = match cond {
                Value::Bool(b) => b,
                _ => {
                    eprintln!("if: condition is not boolean: {cond:?}");
                    return Err(());
                }
            };

            if b {
                for stat in &branch.then {
                    match self.eval_stat(func_binds, stat)? {
                        ControlFlow::None => (),
                        cf => return Ok(cf),
                    }
                }
                reach_otherwise = false;
                break;
            }
        }

        if reach_otherwise {
            for stat in otherwise {
                match self.eval_stat(func_binds, stat)? {
                    ControlFlow::None => (),
                    cf => return Ok(cf),
                }
            }
        }

        Ok(ControlFlow::None)
    }

    fn eval_cd_block(
        &mut self,
        func_binds: &FuncBinds,
        path: &Expr,
        stats: &Vec<Stat>,
    ) -> Result<ControlFlow, ()> {
        let path = self.eval_expr(func_binds, path)?;

        let path: PathBuf = match path {
            Value::Symbol(path) => path.into(),
            Value::String(path) => path.into(),
            token => {
                eprintln!("cd: path should be string or symbol instead of {token:?}");
                return Err(());
            }
        };

        let old_cwd = std::mem::replace(&mut self.cwd, Some(path.to_owned()));

        for stat in stats {
            match self.eval_stat(func_binds, stat)? {
                ControlFlow::None => (),
                cf => return Ok(cf),
            }
        }

        self.cwd = old_cwd;
        Ok(ControlFlow::None)
    }

    fn eval_foreach(
        &mut self,
        func_binds: &FuncBinds,
        iterable: &Expr,
        iterator: &String,
        body: &Vec<Stat>,
    ) -> Result<ControlFlow, ()> {
        let iterable = self.eval_expr(func_binds, iterable)?;

        Ok(match iterable {
            Value::Array(vec) => self.iterate_boxed_array(&vec, iterator, body, func_binds)?,
            _ => {
                eprintln!("for: not iterable: {iterable:?}");
                return Err(());
            }
        })
    }

    fn iterate_boxed_array(
        &mut self,
        vec: &Vector<Value>,
        iterator: &String,
        body: &Vec<Stat>,
        func_binds: &FuncBinds,
    ) -> Result<ControlFlow, ()> {
        for (i, each_val) in vec.iter().enumerate() {
            self.binds.insert(iterator.to_owned(), each_val.clone());
            self.binds
                .insert("index".to_string(), Value::Integer(i as i64));

            for stat in body {
                match self.eval_stat(func_binds, stat)? {
                    ControlFlow::None => (),
                    ControlFlow::Return(value) => return Ok(ControlFlow::Return(value)),
                    ControlFlow::Continue => break,
                    ControlFlow::Break => return Ok(ControlFlow::None),
                }
            }
        }

        Ok(ControlFlow::None)
    }

    fn eval_expr(&self, func_binds: &FuncBinds, arg: &Expr) -> Result<Value, ()> {
        Ok(match arg {
            Expr::And { conds } => {
                for cond in conds {
                    let cond = self.eval_expr(func_binds, cond)?;

                    match cond {
                        Value::Bool(b) => {
                            if !b {
                                return Ok(Value::Bool(false));
                            }
                        }
                        _ => {
                            eprintln!("and: expect boolean: {cond:?}");
                            return Err(());
                        }
                    }
                }

                Value::Bool(true)
            }
            Expr::Or { conds } => {
                for cond in conds {
                    let cond = self.eval_expr(func_binds, cond)?;

                    match cond {
                        Value::Bool(b) => {
                            if b {
                                return Ok(Value::Bool(true));
                            }
                        }
                        _ => {
                            eprintln!("or: expect boolean: {cond:?}");
                            return Err(());
                        }
                    }
                }

                Value::Bool(false)
            }
            Expr::FuncCall { name, args } => {
                let Some(func) = func_binds.binds.get(name) else {
                    eprintln!("undefined function: {name}");
                    return Err(());
                };

                self.eval_func_call(func_binds, func, args)?
            }
            Expr::Value(value) => value.clone(),
            Expr::LoadVar { name } => {
                let Some(value) = self.find(name) else {
                    eprintln!("undefined variable: {name}");
                    return Err(());
                };
                value.clone()
            }
        })
    }
}

#[derive(Debug)]
#[must_use]
#[derive(Default)]
enum ControlFlow {
    #[default]
    None,
    Return(Value),
    Break,
    Continue,
}
